30天自制操作系统:第四天 C语言与画面显示的练习 |
您所在的位置:网站首页 › invalid type argument of unary翻译 › 30天自制操作系统:第四天 C语言与画面显示的练习 |
1.用C语言实现内存写入(harib01a)
章节开始展示了一个用于写入内存的汇编函数: _write_mem8 栈压入一个参数,如果有汇编基础这块应该好理解,没有也没关系,这里给出压栈CPU执行的指令: 压入第二个参数 addr,与第个二步骤一样: 压入返回地址:将当前代码区调用指令的下一条指令地址压入栈中,供函数返回时继续执行 到此,就能够理解为什么第一个参数存放的位置是 [ESP + 4] 了吧,跟栈的特点有关(栈从高地址向低地址压入元素)以及push指令的执行。 依次类推,要访问data参数,那么需要偏移2 * 4 = 8:[ESP + 8] 函数类似于C语言中的"write_mem8(0x1234, 0x56);“语句,执行动作相当于"MOV BYTE[0x1234], 0x56”,也就是将数值0x56写入到0x1234内存字节单元中。 想要取得用参数指定的数值0x1234或0x56的内容,就用MOV读入寄存器。因为CPU已经是32位模式,所以我们积极用32位寄存器,16位寄存器也可以使用, 不过机器语言的字节数会增加,执行速度也会变慢。 在指定内存的时候,如果使用16位寄存器:[CX] 或 [SP] 之类的就会出错,但使用32位寄存器,类似:[ECX]、[ESP]都没问题。 需要注意:如果与C语言联合使用,有的寄存器可以自由使用,有的寄存器不能随便使用,能自由使用的只有 EAX、ECX、EDX 这三个,至于其他寄存器,只能使用其值,而不能改变其值,因为这些寄存器在C语言编译后后生成的机器语言中,用于记忆非常重要的值。因此这次我们只用EAX和ECX。 如下图,作者还在naskfunc.nas增加了这条语句(描黑部分): 虽然表明是486用,但并不是说会出现仅能在486中执行的机器语言,所以该模式下只使用16位寄存器,也能成为8086中可以执行的机器语言。
打开 !cons_nt.bat 文件,输入make run 指令: 这里对bootpack.c程序做了一点修改: 打开 harib01b下的!cons_nt.bat文件,输入 make run 指令: 如果将 write_mem(i, i & 0x0f **); 替换成 i = i & 0x0f;按照之前执行的步骤输入make run,会发现出现 “invalid type argument of ‘unary *’”,翻译过来就是无效类型,出现了类型错误。 先看看这条汇编语句: mov [0x1234], 0x56; 很明显这是不允许的,因为对于一块内存空间没有指明大小(内存是连续的,可以将0x1234这一字节单元赋值为0x56, 当然也可以将0x1234以及相邻的内存单元0x1235赋值成0x56),如何告诉计算机这是BYTE? 对于汇编语言,使用 mov byte ptr [0x1234], 0x56即可 对于C语言,使用char* p,1字节类型的指针即可。 **char *p: 用于BYTE类地址,short p:用于WORD类地址,int p:用于DWORD类地址。 注:“char i;” 类似AL的1字节变量,“short i;” 是类似AX的2字节变量,“int i;” 是类似EAX的4字节变量。 而不论是 “char *p”, 还是 “short *p”, 或是 “int *p”, 变量p都是4字节,这是因为p是用于记录地址的变量。在汇编语言中,地址也像ECX一样,用4字节的寄存器来指定,所以也是4字节。 使用make run执行以下程序: void HariMain(void) { int i; /* 変数宣言。iという変数は、32ビットの整数型 */ char *p; /* pという変数は、BYTE [...]用の番地 */ for (i = 0xa0000; i io_hlt(); } }编译时会有一个警告:bootpack.c:10: warning: assignment makes pointer from integer without a cast 如果定义了:p = (char*) i; 将上式代入到 *p = i & 0x0f中,可以得到: ((char) i) = i & 0x0f,这样的写法也没问题,而且还省去了p变量,与汇编语言:BYTE[i] = i & 0x0f; 有点类似。就是理解起来比较困难。 COLUMN-3 还是不能理解指针使用C语言完成以下功能: MOV BYTE [i], (i & 0x0f),也就是向内存的第 i 号地址写入 i & 0x0f 的结果,C语言程序可以写为: 另一个关于声明的问题:在C语言中,如果不声明变量就不能使用。在C语言中,声明了十个变量,就可以使用十个变量,没问题。 既然如此,那为什么只声明了 “char p;” 却不仅能使用p,还可以使用p,回到汇编语言: MOV ECX, I MOV BYTE [ECX], (i & 0x0f)
说明了绘制条纹图案的部分还可以写成这样:
这里使用了320 x 200的8位颜色模式,该模式允许程序员随意指定0 ~ 255 的数字所对应的颜色,比如25号颜色对应#ffffff, 26号颜色对应#123456,这种方式叫做调色板(palette)。 需要注意的是set_palette函数的第三个参数rgb指针必须是无符号类型(unsigned char), 否则如果想表达255会被记为-1。 函数set_palette
CPU的管脚与内存相连,如果仅仅是与内存相连,CPU只能完成计算和存储的功能。实际上,CPU还要对键盘的输入有响应,要通过网卡从网络取得信息,通过声卡发送音乐数据,向软盘写入信息等,这些都是设备,它们当然也需要连接到CPU上。 既然CPU与设备相连,那么就有向这些设备发送电信号,或者从这些设 备取得信息的指令。向设备发送电信号的是OUT指令;从设备取得电气 信号的是IN指令。正如为了区别不同的内存要使用内存地址一样,在 OUT指令和IN指令中,为了区别不同的设备,也要使用设备号码。设备 号码在英文中称为port(端口)。port原意为“港口”,这里形象地将CPU 与各个设备交换电信号的行为比作了船舶的出港和进港。 0x03c8、0x03c9的解释上述程序中,为什么使用了这些地址。
这是由名为FLAGS的16位寄存器扩展而来的32位寄存器。FLAGS是存储进位标志和中断标志等标志的寄存器。进位标志可以通过JC或JNC等跳转指令来简单地判断到底是0还是1。但对于中断标志,没有类似的JI或JNI命令,所以只能读入EFLAGS,再检查第9位是0还是1。顺便说一下,进位标志是EFLAGS的第0位。 今天的博客有点缩水(后面两小节没啥可以写的,所以直接删除了),主要原因是很多内容如今并没有能力自己清晰写出来,只能是照搬原书的解析,笔者是想借助此种方式融入一些自己的想法,可以加深自己的理解,也是为了记录做成一个OS的路程。Over |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |